/*
 * Copyright © OpenAtom Foundation.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package io.iec.edp.caf.databaseobject.rtmanager;

import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.databaseobject.api.entity.*;
import io.iec.edp.caf.databaseobject.api.service.IDatabaseObjectDtService;
import io.iec.edp.caf.databaseobject.common.DatabaseObjectCheckUtil;
import io.iec.edp.caf.databaseobject.common.DatabaseObjectCommonUtil;
import io.iec.edp.caf.databaseobject.common.exception.DboException;
import io.iec.edp.caf.databaseobject.common.exception.DboExceptionCode;
import io.iec.edp.caf.databaseobject.rtmanager.repository.GspDatabaseObjectDtRepository;
import io.iec.edp.caf.rpc.api.service.RpcClient;

import java.io.IOException;
import java.util.LinkedHashMap;

/**
 * @author liu_wei
 */
public class DatabaseObjectDtServiceImpl implements IDatabaseObjectDtService {

    private GspDatabaseObjectDtRepository repository;
    private RpcClient client = SpringBeanUtils.getBean(RpcClient.class);

    public DatabaseObjectDtServiceImpl(GspDatabaseObjectDtRepository repository) {
        this.repository = repository;
    }

    @Override
    public AbstractDatabaseObject getDtDatabaseObject(String dboId) {
        try {
            GspDatabaseObjectDT databaseObjectDt = repository.findById(dboId).orElse(null);
            if (databaseObjectDt == null) {
                return null;
            }
            String content = databaseObjectDt.getContent();
            DatabaseObjectCommonUtil commonUtil = new DatabaseObjectCommonUtil();
            return commonUtil.deserialze(content);
        } catch (Throwable e) {
            throw new RuntimeException("根据id[" + dboId + "]获取设计时DBO异常:" + e.getMessage(), e);
        }
    }

    @Override
    public AbstractDatabaseObject getDtDatabaseObject(String dboId, String su) {
        try {
            LinkedHashMap<String, Object> parameters = new LinkedHashMap<String, Object>();
            parameters.put("dboId", dboId);
            return client.invoke(AbstractDatabaseObject.class, "io.iec.edp.caf.databaseobject.rpcapi.service.DatabaseObjectRpcService.getDtDatabaseObject", su, parameters, null);
        } catch (Throwable e) {
            throw new RuntimeException("根据id[" + dboId + "]获取设计时DBO异常:" + e.getMessage(), e);
        }
    }

    @Override
    public AbstractDatabaseObject getDtDatabaseObjectByCode(String dboCode) {
        try {
            GspDatabaseObjectDT databaseObjectDt = repository.findByCode(dboCode);
            if (databaseObjectDt == null) {
                return null;
            }
            String content = databaseObjectDt.getContent();
            DatabaseObjectCommonUtil commonUtil = new DatabaseObjectCommonUtil();
            return commonUtil.deserialze(content);
        } catch (Throwable e) {
            throw new RuntimeException("根据code[" + dboCode + "]获取设计时DBO异常:" + e.getMessage(), e);
        }
    }

    @Override
    public AbstractDatabaseObject getDtDatabaseObjectByCode(String dboCode, String su) {
        try {
            LinkedHashMap<String, Object> parameters = new LinkedHashMap<String, Object>();
            parameters.put("dboCode", dboCode);
            return client.invoke(AbstractDatabaseObject.class, "io.iec.edp.caf.databaseobject.rpcapi.service.DatabaseObjectRpcService.getDtDatabaseObjectByCode", su, parameters, null);
        } catch (Throwable e) {
            throw new RuntimeException("根据code[" + dboCode + "]获取设计时DBO异常:" + e.getMessage(), e);
        }
    }

    @Override
    public void saveDtDatabaseObject(AbstractDatabaseObject databaseObject) {
        try {
            DatabaseObjectCheckUtil checkUtil = new DatabaseObjectCheckUtil();
            checkUtil.checkDatabaseObjectAvailable(databaseObject);
            AbstractDatabaseObject result = getDtDatabaseObjectByCode(databaseObject.getCode());
            if (!isExistDatabaseObjectDt(databaseObject.getId())) {
                if (result != null) {
                    throw new RuntimeException("数据库中已存在编号为" + databaseObject.getCode() + "的设计时DBO，如果原有DBO已废弃，请删除后重试。否则，修改Code后重试");
                }
                createDatabaseObject(databaseObject);
            } else {
                if (result != null) {
                    if (result.getId().equals(databaseObject.getId())) {
                        updateDtDatabaseObject(databaseObject);
                    } else {
                        throw new RuntimeException("数据库中除了当前设计时DBO，还有重编号的设计时DBO，请确认。编号为：" + databaseObject.getCode());
                    }
                }
            }
        } catch (Throwable e) {
            throw new RuntimeException("向数据库表中保存设计时DBO报错，编号为：" + databaseObject.getCode(), e);
        }
    }

    @Override
    public void saveDtDatabaseObject(AbstractDatabaseObject databaseObject, String su) {
        try {
            DatabaseObjectCommonUtil commonUtil = new DatabaseObjectCommonUtil();
            String object = commonUtil.serialze(databaseObject);
            LinkedHashMap<String, Object> parameters = new LinkedHashMap<String, Object>();
            parameters.put("databaseObject", object);
            client.invoke(String.class, "io.iec.edp.caf.databaseobject.rpcapi.service.DatabaseObjectRpcService.saveDtDatabaseObject", su, parameters, null);
        } catch (Throwable e) {
            throw new RuntimeException("保存设计时DBO记录报错：" + e.getMessage(), e);
        }

    }

    @Override
    public void deleteDtDatabaseObject(String dboId) {
        try {
            repository.deleteGspDatabaseObjectDtById(dboId);
        } catch (Throwable e) {
            throw new DboException(DboExceptionCode.DeleteDboRecordError,"删除设计时DBO记录报错：" + e.getMessage(), null, ExceptionLevel.Warning, true);
        }
    }

    @Override
    public void deleteDtDatabaseObject(String dboId, String su) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<String, Object>();
        parameters.put("dboId", dboId);
        client.invoke(String.class, "io.iec.edp.caf.databaseobject.rpcapi.service.DatabaseObjectRpcService.deleteDtDatabaseObject", su, parameters, null);
    }

    private void updateDtDatabaseObject(AbstractDatabaseObject databaseObject) throws IOException {
        GspDatabaseObjectDT result = repository.findById(databaseObject.getId()).orElse(null);
        DatabaseObjectCommonUtil commonUtil = new DatabaseObjectCommonUtil();
        if (result != null) {
            result.setContent(commonUtil.serialze(databaseObject));
            result.setLastModifier(CAFContext.current.getUserId());
            result.setLastModifiedTime(java.time.LocalDateTime.now());
            repository.save(result);
        }
        else
        {
            throw new RuntimeException(String.format("数据库中不存在要更新的设计时DBO，DBO的Id为：%1$s", databaseObject.getId()));
        }
    }

    private void  createDatabaseObject(AbstractDatabaseObject databaseObject) {
        checkForNullReference(databaseObject, "databaseObject");
        checkForEmptyString(databaseObject.getCode(), "Code");
        if (databaseObject.getType() == DatabaseObjectType.Table || databaseObject.getType() == DatabaseObjectType.TempTable) {
            checkForNullReference(((DatabaseObjectTable) databaseObject).getColumns(), "columns");
        }
        try {
            GspDatabaseObjectDT gspDatabaseObject = buildGspDatabaseObject(databaseObject);
            gspDatabaseObject.setCreator(CAFContext.current.getUserId());
            gspDatabaseObject.setLastModifier(CAFContext.current.getUserId());
            gspDatabaseObject.setCreatedTime(java.time.LocalDateTime.now());
            gspDatabaseObject.setLastModifiedTime(java.time.LocalDateTime.now());
            repository.save(gspDatabaseObject);
        } catch (Throwable e) {
            throw new RuntimeException(String.format("向数据库表中新增数据库对象报错，数据库对象编号为：%1$s", databaseObject.getCode()), e);
        }
    }

    private GspDatabaseObjectDT buildGspDatabaseObject(AbstractDatabaseObject databaseObject) throws IOException {
        GspDatabaseObjectDT result = new GspDatabaseObjectDT();
        result.setId(databaseObject.getId());
        result.setCode(databaseObject.getCode());
        result.setName(databaseObject.getName());
        if (databaseObject.getType() == DatabaseObjectType.Table) {
            DatabaseObjectTable table = (DatabaseObjectTable) databaseObject;
            result.setBusinessObjectId(table.getBusinessObjectId());
        } else {
            DatabaseObjectView view = (DatabaseObjectView) databaseObject;
            result.setBusinessObjectId(view.getBusinessObjectId());
        }
        result.setType(databaseObject.getType().getValue());
        DatabaseObjectCommonUtil commonUtil = new DatabaseObjectCommonUtil();
        result.setContent(commonUtil.serialze(databaseObject));
        return result;
    }

    private void checkForNullReference(Object objectValue, String objectName) throws RuntimeException {
        if (objectValue == null) {
            throw new RuntimeException(objectName + "不能为null");
        }
    }

    private void checkForEmptyString(String strValue, String strName) throws RuntimeException {
        if (strValue == null || strValue.length() <= 0) {
            throw new RuntimeException(strName + "不能为空");
        }
    }

    @Override
    public boolean isExistDatabaseObjectDt(String dboId) {
        try {
            return repository.existsById(dboId);
        } catch (Throwable e) {
            throw new DboException(DboExceptionCode.JudgeFailed,"根据ID判断设计时DBO是否存在报错，数据库对象ID为：" + dboId, null, ExceptionLevel.Warning, true);
        }
    }

    @Override
    public boolean isExistDatabaseObjectDt(String dboId, String su) {
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<String, Object>();
        parameters.put("dboId", dboId);
        return client.invoke(boolean.class, "io.iec.edp.caf.databaseobject.rpcapi.service.DatabaseObjectRpcService.isExistDatabaseObjectDt", su, parameters, null);
    }
}
